package cn.com.dcsgo.service.impl;

import cn.com.dcsgo.dto.SysMenuAddDTO;
import cn.com.dcsgo.dto.SysMenuUpdateDTO;
import cn.com.dcsgo.exception.BusinessException;
import cn.com.dcsgo.validation.menu.MenuValidationResult;
import cn.com.dcsgo.validation.menu.MenuValidatorFactory;
import cn.com.dcsgo.validation.menu.validator.MenuValidator;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.com.dcsgo.domain.SysMenu;
import cn.com.dcsgo.service.SysMenuService;
import cn.com.dcsgo.mapper.SysMenuMapper;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 针对表【sys_menu(菜单管理)】的数据库操作Service实现
 *
 * @author Dcsgo
 * @since 2025-09-22 15:09:11
 */
@Service
@CacheConfig(cacheNames = {"cn.com.dcsgo.service.impl.SysMenuServiceImpl"})
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu>
        implements SysMenuService {
    @Resource
    private SysMenuMapper sysMenuMapper;

    @Resource
    private MenuValidatorFactory menuValidatorFactory;

    @Override
    //@Cacheable(key = "#userId")
    public List<SysMenu> getMenuListByUserId(Long userId) {
        // 管理员可以查看所有目录菜单
        if (userId.equals(1L)) {
            return buildMenuTree(
                    sysMenuMapper.selectList(
                            new LambdaQueryWrapper<SysMenu>()
                                    .in(SysMenu::getType, 0, 1)
                                    .orderByAsc(SysMenu::getParentId, SysMenu::getOrderNum)
                    ), 0L);
        }
        List<SysMenu> menuList = sysMenuMapper.selectListByUserId(userId);
        if (!CollectionUtils.isEmpty(menuList)) {
            // 构建菜单树（层级关系）
            return buildMenuTree(menuList, 0L);
        }
        return menuList;
    }

    @Override
    public List<SysMenu> getMenusByUserId(Long userId) {
        if (userId.equals(1L)) {
            return sysMenuMapper.selectList(
                    new LambdaQueryWrapper<SysMenu>()
                            .orderByAsc(SysMenu::getParentId, SysMenu::getOrderNum)
            );
        }
        return sysMenuMapper.selectAllByUserId(userId);
    }

    @Override
    public int save(SysMenuAddDTO sysMenuAddDTO) {
        //根据不同的菜单类型做相应的参数校验
        MenuValidator menuValidator = menuValidatorFactory.getMenuValidator(sysMenuAddDTO.getType(), BeanUtil.beanToMap(sysMenuAddDTO));
        MenuValidationResult validateResult = menuValidator.validate();
        if (!validateResult.isValid()) {
            throw new BusinessException(validateResult.getErrorMessage());
        }
        SysMenu sysMenu = new SysMenu();
        BeanUtil.copyProperties(sysMenuAddDTO, sysMenu);
        return baseMapper.insert(sysMenu);
    }

    @Override
    public List<SysMenu> getDirectoryAndMenuListByUserId(Long userId) {
        if (userId == 1L) {
            return sysMenuMapper.selectList(
                    new LambdaQueryWrapper<SysMenu>()
                            .in(SysMenu::getType, 0, 1)
                            .orderByAsc(SysMenu::getParentId, SysMenu::getOrderNum)
            );
        }
        return sysMenuMapper.selectListByUserId(userId);
    }

    @Override
    public int update(SysMenuUpdateDTO sysMenuUpdateDTO) {
        // 菜单类型和父菜单ID同时为空，不用做菜单层级关系校验；反之需要
        if (!(sysMenuUpdateDTO.getParentId() == null && sysMenuUpdateDTO.getType() == null)) {
            if (sysMenuUpdateDTO.getMenuId().equals(sysMenuUpdateDTO.getParentId())) {
                throw new BusinessException("上级菜单不能选择自己");
            }

            // 菜单类型和父菜单ID都变了，直接走校验
            if (sysMenuUpdateDTO.getType() != null && sysMenuUpdateDTO.getParentId() != null) {
                MenuValidationResult validateResult = menuValidatorFactory.getMenuValidator(sysMenuUpdateDTO.getType(), BeanUtil.beanToMap(sysMenuUpdateDTO)).validate();
                if (!validateResult.isValid()) {
                    throw new BusinessException(validateResult.getErrorMessage());
                }
            } else {
                // 只改菜单类型要考虑父菜单ID是否与新的菜单类型匹配，例如从按钮改为菜单父菜单不能是目录也不能是按钮
                // 从目录改为按钮或目录父菜单ID不能是0
                // 如果是只改父菜单ID，要考虑新的父菜单是否与老菜单类型匹配
                // ...... 也不是不能写，但是没必要，徒增代码复杂度
                throw new BusinessException("此次菜单更新较为复杂，请采用删除加重新添加的方式实现此次操作所期望的更新结果");
            }
        }
        //TODO：修改时旧属性的处理。例如从按钮改为菜单授权标识应该置null（不清不影响因为只有当菜单类型是按钮时权限标识才有意义）
        SysMenu menu = new SysMenu();
        BeanUtil.copyProperties(sysMenuUpdateDTO, menu);
        return baseMapper.updateById(menu);
    }

    @Override
    public int waryRemoveById(Long menuId) {
        boolean hasChild = baseMapper.exists(
                new LambdaQueryWrapper<SysMenu>()
                        .eq(SysMenu::getParentId, menuId)
        );
        if (hasChild) {
            throw new BusinessException("当前菜单有子菜单，不允许被删除");
        }
        return baseMapper.deleteById(menuId);
    }

    /**
     * 构建菜单树（支持多级菜单的构建）
     *
     * @param menuList 菜单列表
     * @param rootId   根节点ID，一级菜单为0
     * @return 菜单树
     */
    private List<SysMenu> buildMenuTree(List<SysMenu> menuList, long rootId) {
        // 将当前递归中的所有顶层节点找出来
        List<SysMenu> topMenus = menuList.stream().filter(menu -> menu.getParentId() == rootId).collect(Collectors.toList());
        // 递归构建子菜单树
        topMenus.forEach(topMenu -> topMenu.getList().addAll(buildMenuTree(menuList, topMenu.getMenuId())));
        return topMenus;
    }
}




